﻿using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace FolderFileMenu
{
    internal sealed class NativeWin32
    {
        public const Int32 WM_SYSCOMMAND = 0x112;
        public const Int32 MF_SEPARATOR = 0x800;
        public const Int32 MF_BYPOSITION = 0x400;
        public const Int32 MF_STRING = 0x0;
        public const Int32 IDM_CUSTOMITEM1 = 1000;
        public const Int32 IDM_CUSTOMITEM2 = 1001;
        public const uint SHGFI_ICON = 0x100;
        public const uint SHGFI_LARGEICON = 0x0; // 'Large icon
        public const uint SHGFI_SMALLICON = 0x1; // 'Small icon

        [DllImport("user32.dll")]
        public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool AppendMenu(IntPtr hMenu, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);

        [DllImport("Shell32.dll")]
        public extern static int ExtractIconEx(string libName, int iconIndex, IntPtr[] largeIcon, IntPtr[] smallIcon, uint nIcons);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public extern static bool DestroyIcon(IntPtr handle);

        [DllImport("shell32.dll")]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEINFO
        {
            public IntPtr hIcon;
            public IntPtr iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }

        public struct IconResult
        {
            public readonly Icon icon;
            public readonly int total;
            public IconResult(Icon _icon, int _total)
            {// erspart die Deklaration der Variablen innerhalb der anwendenden Methode, z.B.: IconResult iResult; ... iResult.icon ...
                icon = _icon;
                total = _total;
            }
        }

        public static IconResult ExtractIconFromExe(string file, bool large, int index)
        {
            int readIconCount = 0;
            IntPtr[] hIconEx = new IntPtr[1] { IntPtr.Zero };
            try
            {
                int totalNumber = ExtractIconEx(file, -1, null, null, 1); // return value = number of icons contained in the specified file
                if (index >= totalNumber)
                {
                    index = index >= totalNumber ? 0 : index; // zero-based index of the first icon to extract
                }
                if (large) { readIconCount = ExtractIconEx(file, index, hIconEx, null, 1); }
                else { readIconCount = ExtractIconEx(file, index, null, hIconEx, 1); }
                if (readIconCount > 0 && hIconEx[0] != IntPtr.Zero)
                {
                    return new IconResult((Icon)Icon.FromHandle(hIconEx[0]).Clone(), totalNumber); // GET FIRST EXTRACTED ICON
                }
                else { return new IconResult(null, 0); } // NO ICONS READ
            }
            catch (Exception ex) { throw new ApplicationException("Could not extract icon.", ex); }
            finally { foreach (IntPtr ptr in hIconEx) { if (ptr != IntPtr.Zero) { DestroyIcon(ptr); } } } // RELEASE RESOURCES
        }

    }
}


